﻿using System;
using System.IO;
using System.Text;
using System.Linq;
using System.Xml.Linq;
using System.Collections.Generic;

namespace SolutionGeneratorHelper
{
    internal class Project : CommonNode, IProject
    {
        private List<IProject> m_dependencies;
        internal List<IProject> Dependencies { get => m_dependencies; set => m_dependencies = value; }

        private XDocument m_projXmlTree;
        internal XDocument ProjXmlTree { get => m_projXmlTree; set => m_projXmlTree = value; }

        private XNamespace m_projXmlNamespace;
        internal XNamespace ProjXmlNamespace { get => m_projXmlNamespace; set => m_projXmlNamespace = value; }

        private XElement m_projXmlDependencies;
        public XElement ProjXmlDependencies { get => m_projXmlDependencies; set => m_projXmlDependencies = value; }

        private Field m_projectName;
        public Field ProjectName
        {
            get => m_projectName;
            set => SetField(ref m_projectName, value);
        }

        private Field m_fileName;
        public Field FileName
        {
            get => m_fileName;
            set => SetField(ref m_fileName, value);
        }

        private Field m_projectGuid;
        public Field ProjectGuid
        {
            get => m_projectGuid;
            set => SetField(ref m_projectGuid, value);
        }

        internal bool Valid
        {
            get => null != ProjXmlTree;
        }

        protected override void Init()
        {
            base.Init();
            m_dependencies = new List<IProject>();
        }


        #region Parse Interface

        private static void ParseProjectDependencies(Project _proj, string _content)
        {
            if (null == _proj.ProjXmlTree)
                _proj.ProjXmlTree = XDocument.Parse(_content);

            if (null == _proj.ProjXmlNamespace)
                _proj.ProjXmlNamespace = _proj.ProjXmlTree.Root.GetDefaultNamespace();

            if (null == _proj.ProjXmlDependencies)
            {
                foreach (XElement xe in _proj.GetCurrentXElements(_proj.ProjXmlTree.Root, "ItemGroup"))
                {
                    XElement temp = _proj.GetFirstXElement(xe, "ProjectReference");
                    if (null != temp)
                    {
                        _proj.ProjXmlDependencies = xe;
                        break;
                    }
                }
            }

            if (null == _proj.ProjXmlDependencies)
                return;

            foreach (XElement xe in _proj.GetCurrentXElements(_proj.ProjXmlDependencies, "ProjectReference"))
            {
                string projectFile = null;
                string projectName = null;
                string projectGuid = null;

                XAttribute fileAttr = xe.Attribute("Include");
                if (null != fileAttr)
                    projectFile = fileAttr.Value;

                XElement nameElem = _proj.GetFirstXElement(xe, "Name");
                if (null != nameElem)
                    projectName = nameElem.Value;

                XElement GuidElem = _proj.GetFirstXElement(xe, "Project");
                if (null != GuidElem)
                    projectGuid = GuidElem.Value.Substring(1, GuidElem.Value.Length-2);


                if (null != projectFile && null != projectName && null != projectGuid)
                {
                    ProjectRef projRef = new ProjectRef(
                        new Field("FileName", projectFile),
                        new Field("ProjectName", projectName),
                        new Field("ProjectGuid", projectGuid)
                    );
                    _proj.InsertOrUpdateDependencyProjectRef(projRef, false);
                }
                else
                {
                    Console.WriteLine("FileName is {0}", projectFile);
                    Console.WriteLine("ProjectGuid is {0}", projectGuid);
                    Console.WriteLine("ProjectName is {0}", projectName);
                }
            }

        }

        internal virtual void ParseProjectDependencies(string _content)
        {
            ParseProjectDependencies(this, _content);
        }

        private static void ParseProject(Project _proj, string _content)
        {
            // parse the xml;
            _proj.ProjXmlTree = XDocument.Parse(_content);
            _proj.ProjXmlNamespace = _proj.ProjXmlTree.Root.GetDefaultNamespace();

            XElement xlmElem;
            // parse project guid;
            xlmElem = _proj.GetFirstXElement(_proj.ProjXmlTree.Root, "ProjectGuid");
            if (null != xlmElem)
                _proj.ProjectGuid = new Field("ProjectGuid", xlmElem.Value.Substring(1, xlmElem.Value.Length - 2));

            // parse project name;
            xlmElem = _proj.GetFirstXElement(_proj.ProjXmlTree.Root, "AssemblyName");
            if (null != xlmElem)
                _proj.ProjectName = new Field("ProjectName", xlmElem.Value);

            _proj.ParseProjectDependencies(_content);
        }

        internal virtual void ParseProject(string _content)
        {
            ParseProject(this, _content);
        }

        private static void ParseProjectFile(Project _proj, string _projFile)
        {
            string path = Environment.CurrentDirectory + "/" + _projFile;
            if (File.Exists(path))
            {
                string content = File.ReadAllText(path);
                _proj.ParseProject(content);
            }
        }

        internal virtual void ParseProjectFile(string _projFile)
        {
            FileName = new Field("FileName", _projFile);
            ParseProjectFile(this, _projFile);
        }

        #endregion


        #region XElement Interface

        internal IEnumerable<XElement> GetCurrentXElements(XElement _root, string _name)
        {
            if (null == ProjXmlNamespace)
            {
                Console.WriteLine("ProjXmlNamespace is null");
                return null;
            }
            XName nodeName = ProjXmlNamespace.GetName(_name);
            IEnumerable<XElement> rt = _root.Elements(nodeName);
            return rt;
        }

        internal IEnumerable<XElement> GetAllXElements(XElement _root, string _name)
        {
            if (null == ProjXmlNamespace)
            {
                Console.WriteLine("ProjXmlNamespace is null");
                return null;
            }
            XName nodeName = ProjXmlNamespace.GetName(_name);
            IEnumerable<XElement> rt = _root.Descendants(nodeName);
            return rt;
        }

        internal XElement GetFirstXElement(XElement _root, string _name)
        {
            IEnumerable<XElement> rt = GetAllXElements(_root, _name);
            if (null == rt)
                return null;
            return rt.FirstOrDefault();
        }

        #endregion


        #region Project Ref Interface

        internal ProjectRef GetCurrentProjectRef()
        {
            return new ProjectRef(FileName, ProjectName, ProjectGuid);
        }

        #endregion


        #region Dependency Project Ref Interface

        internal bool ContainsDependencyProjectRef(ProjectRef _projRef)
        {
            bool rt = false;
            foreach (ProjectRef projRef in Dependencies)
            {
                if (_projRef.ProjectGuid.Value == projRef.ProjectGuid.Value)
                {
                    rt = true;
                    break;
                }
            }
            return rt;
        }


        internal virtual ProjectRef GetDependencyProjectRef(Field _projGuid)
        {
            ProjectRef rt = null;
            foreach (ProjectRef projRef in Dependencies)
            {
                if (_projGuid.Value == projRef.ProjectGuid.Value)
                {
                    rt = projRef;
                    break;
                }
            }
            return rt;
        }


        internal void InsertProjectFile(string _projFile)
        {
            Project proj = new Project();
            proj.ParseProjectFile(_projFile);
            if (proj.Valid)
                InsertOrUpdateDependencyProjectRef(proj.GetCurrentProjectRef());
        }


        internal void InsertProject(string _content)
        {
            Project proj = new Project();
            proj.ParseProject(_content);
            if (proj.Valid)
                InsertOrUpdateDependencyProjectRef(proj.GetCurrentProjectRef());
        }


        internal void InsertOrUpdateDependencyProjectRef(ProjectRef _projRef, bool _bUpdate = true)
        {
            if (ContainsDependencyProjectRef(_projRef))
            {
                Console.WriteLine("Insert an exist project ref to Dependencies so update it");
                ProjectRef projRef = GetDependencyProjectRef(_projRef.ProjectGuid);
                projRef.FileName = _projRef.FileName;
                projRef.ProjectName = _projRef.ProjectName;
            }
            else
            {
                Console.WriteLine("Insert a project ref to Dependencies");
                Dependencies.Add(_projRef);
            }

            if (_bUpdate)
                InsertOrUpdateXmlDependencyProjectRef(_projRef);
        }

        internal void InsertOrUpdateXmlDependencyProjectRef(ProjectRef _projRef)
        {
            if (null == ProjXmlDependencies)
            {
                ProjXmlDependencies = new XElement(ProjXmlNamespace.GetName("ItemGroup"));
                ProjXmlTree.Root.Add(ProjXmlDependencies);
            }

            XElement projXmlDep = null;
            foreach (XElement xe in GetCurrentXElements(ProjXmlDependencies, "ProjectReference"))
            {
                XElement GuidElem = GetFirstXElement(xe, "Project");
                if (null != GuidElem && _projRef.ProjectGuid.Value == GuidElem.Value.Substring(1, GuidElem.Value.Length - 2))
                {
                    projXmlDep = xe;
                    break;
                }
            }

            if (null == projXmlDep)
            {
                projXmlDep = new XElement(
                    ProjXmlNamespace.GetName("ProjectReference"),
                    new XAttribute("Include", _projRef.FileName.Value),
                    new XElement(ProjXmlNamespace.GetName("Project"), "{" + _projRef.ProjectGuid.Value + "}"),
                    new XElement(ProjXmlNamespace.GetName("Name"), _projRef.ProjectName.Value)
                );
                ProjXmlDependencies.Add(projXmlDep);
            }
            else
            {
                projXmlDep.Attribute("Include").Value = _projRef.FileName.Value;
                projXmlDep.Element(ProjXmlNamespace.GetName("Name")).Value = _projRef.ProjectName.Value;
            }
        }

        #endregion

        public override string Serialize()
        {
            if (!Valid) { return null; }
            var str = new Utf8StringWriter();
            ProjXmlTree.Save(str);
            return str.ToString();
        }
    }
}
